package com.kaluli.system.repository;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateCallback;
import org.springframework.orm.hibernate4.HibernateTemplate;

import com.kaluli.system.domain.entity.AbstractDomainEntity;
import com.kaluli.system.repository.QueryTerm.QueryType;
import com.kaluli.system.repository.Term.Entry;
import com.kaluli.system.utils.Pageable;



/**
 * 
 * hibernate 公共操作的实现类
 * @author jihprowlet@gmail.com 
 *
 * @param <T>
 */
public abstract class AbstractRepositorySupport<T extends AbstractDomainEntity> implements
		AbstractRepository<T> {
	
		static Logger log = Logger.getLogger(AbstractRepositorySupport.class);

	// @Autowired
		// private SessionFactory sessionFactory;
		@Autowired
		private HibernateTemplate hibernateTemplate;

		
		protected Class<T> itemClass;
		
		public AbstractRepositorySupport()
		{
			itemClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
					.getActualTypeArguments()[0];
		}
		
		
		protected void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
			this.hibernateTemplate = hibernateTemplate;
		}
		
		protected HibernateTemplate getTemplate() {
			return hibernateTemplate;
		}

		@Override
		public T findById(Serializable pk) {
			T entity = (T) getTemplate().get(getEntityClass(), pk);
			return entity;
		}

		@Override
		public Serializable save(T entity) {
			Serializable pk = getTemplate().save(entity);
			return pk;
		}

		@Override
		public void remove(T entity) {
			getTemplate().delete(entity);
		}
		
		public void update(T entity)
		{
			getTemplate().save(entity);
		}

		@Override
		public List<T> findAll() {
			return getTemplate().loadAll(getEntityClass());
		}
		
		@Override
		public T findUniqueResult(Term term) {
			term.setFirst(0);
			term.setLimit(1);
			List<T> results = this.query(term);
			return results.size() > 0 ? results.get(0) : null;
		}
		
		
		@Override
		public List<T> findHql(String hql, Object... params) {
			return (List<T>) getTemplate().find(hql, params);
		}
		
		protected int executeHql(final String hql ,final Object... params){
			return getTemplate().executeWithNativeSession(new HibernateCallback<Integer>() {
				@Override
				public Integer doInHibernate(Session session) throws HibernateException {
					Query query = session.createQuery(hql);				
					for( int i = 0 ; i < params.length ; i++ ) {
						query.setParameter(i, params[i]);
					}
					return query.executeUpdate();
				}
			});
		}

		@Override
		public List<T> findHqlForMap(final String hql, final Map<String, Object> params) {
			return getTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {
				@Override
				public List<T> doInHibernate(Session session) throws HibernateException {
					Query query = session.createQuery(hql);
					for(java.util.Map.Entry<String, Object> entry :  params.entrySet()) {
						query.setParameter(entry.getKey(), entry.getValue());
					}
					return query.list();
				}
			});
		}
		
		
		public List<T> excuteSql(final String sql) {
//			log.info("执行的sql="+sql);
			return getTemplate().executeWithNativeSession(new HibernateCallback<List<T>>() {
				@Override
				public List<T> doInHibernate(Session session) throws HibernateException {
					SQLQuery query = session.createSQLQuery(sql);
					query.addEntity(itemClass);
					return query.list();
				}
			});
		}

		@Override
		public List<T> query(Term term) {
			DetachedCriteria crit = createDetachedCriteria(term);
			List<?> results = null;
			if (term.isPageable()) {
				results = getTemplate().findByCriteria(crit, term.getFirst(),
						term.getLimit());
			} else {
				results = getTemplate().findByCriteria(crit);
			}
			return (List<T>) results;
		}

		private DetachedCriteria createDetachedCriteria(Term term) {
			DetachedCriteria crit = DetachedCriteria.forClass(getEntityClass());

			for (Entry entry : term.getEntries()) {
				switch (entry.getType()) {
				case eq:
					crit.add(Restrictions.eq(entry.getName(), entry.getValue()));
					break;
				case lt:
					crit.add(Restrictions.lt(entry.getName(), entry.getValue()));
					break;
				case lte:
					crit.add(Restrictions.le(entry.getName(), entry.getValue()));
					break;
				case gt:
					crit.add(Restrictions.gt(entry.getName(), entry.getValue()));
					break;
				case gte:
					crit.add(Restrictions.ge(entry.getName(), entry.getValue()));
					break;
				case in:
					Collection array = entry.getObject();
					crit.add(Restrictions.in(entry.getName(), array));
					break;
				case like:
					crit.add(Restrictions.like(entry.getName(), entry.getValue()));
					break;
				case order:
					boolean order = entry.getObject();
					crit.addOrder(order ? Order.asc(entry.getName()) : Order
							.desc(entry.getName()));
					break;
				default:
					break;
				}
			}
			return crit;
		}
		
		@Override
		public Pageable<T> findHqlForMapOfPageable(final String hql, final int number,
				final int limit, final Map<String, Object> params) {
			return findHqlOfPageable(hql, params, null, number, limit);
		}
		
		@Override
		public Pageable<T> findHqlOfPageable(final String hql, final int number, final int limit , final Object... params) {
			return findHqlOfPageable(hql , null, params, number, limit);
		}
		
		/**
		 * 分页查询
		 * @param hql
		 * @param mapParams
		 * @param arrParams
		 * @param number
		 * @param limit
		 * @return
		 */
		protected Pageable<T> findHqlOfPageable(String hql , Map<String, Object> mapParams , Object[] arrParams , Integer number , Integer limit ){
			QueryTerm term = new QueryTerm(hql).params(mapParams).params(arrParams)
					.limit(limit)
					.number(number)
					.type(QueryType.PAGE);
			Pageable<T>  result = query(term);
			return result;
		}
		
		/**
		 * hql 查询封装.
		 * @param term
		 * @return
		 */
		protected <R> R query(final QueryTerm term){
			R result = getTemplate().executeWithNativeSession(new HibernateCallback<R>() {
				public R doInHibernate(Session session) throws HibernateException {
					switch ( term.getType()) {
						case QUERY:
							return term.query(session);
						case ROW:
							return (R) term.rowsCount(session);
						case PAGE:
							return (R) term.pageable(session);
					}
					return null;
				}
			});
			return result;
		}
		
		protected abstract Class<T> getEntityClass();
		
}